/*
 *   Copyright 2012, Thomas Kerber
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
package milk.jpatch.classLevel;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import milk.jpatch.CPoolMap;
import milk.jpatch.Util;
import milk.jpatch.access.AccessFlagsPatch;
import milk.jpatch.attribs.AttributesPatch;

import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.Field;

/**
 * Denotes a field to be modified.
 * @author Thomas Kerber
 * @version 1.0.0
 */
public class FieldModif extends FieldPatch{
    
    private static final long serialVersionUID = 2097120477214527181L;
    
    /**
     * The access flags patch.
     */
    private final AccessFlagsPatch flagsPatch;
    /**
     * The patch of the fields signature (type). Null essentially means ID
     * patch.
     */
    private final String sigPatch;
    /**
     * The attributes patch.
     */
    private final AttributesPatch attribsPatch;
    
    /**
     * Creates.
     * @param name The fields name.
     * @param sigPatch The fields signature patch (null means ID patch,
     *     everything else replace).
     * @param flagsPatch The fields flags patch.
     * @param attribsPatch The fields attributes patch.
     */
    public FieldModif(String name, String sigPatch,
            AccessFlagsPatch flagsPatch, AttributesPatch attribsPatch){
        super(name);
        this.sigPatch = sigPatch;
        this.flagsPatch = flagsPatch;
        this.attribsPatch = attribsPatch;
    }
    
    /**
     * Generates.
     * @param old The old field.
     * @param new_ The new field.
     * @param map The cpool map.
     * @return The field patch.
     */
    public static FieldModif generate(Field old, Field new_, CPoolMap map){
        String name = old.getName();
        String sigPatch = new_.getSignature();
        if(old.getSignature().equals(sigPatch))
            sigPatch = null;
        AccessFlagsPatch flagsPatch = AccessFlagsPatch.generate(
                old.getAccessFlags(),
                new_.getAccessFlags(),
                AccessFlagsPatch.AccessLevel.FIELD);
        AttributesPatch attribsPatch = AttributesPatch.generate(
                old.getAttributes(),
                new_.getAttributes(),
                map);
        
        return new FieldModif(name, sigPatch, flagsPatch, attribsPatch);
    }
    
    /**
     * 
     * @return The patch of the field type. null denotes ID while any other
     *     value will be used instead.
     */
    public String getSignaturePatch(){
        return sigPatch;
    }
    
    /**
     * 
     * @return The flags patch delegated to.
     */
    public AccessFlagsPatch getFlagsPatch(){
        return flagsPatch;
    }
    
    /**
     * 
     * @return The attributes patch delegated to.
     */
    public AttributesPatch getAttributesPatch(){
        return attribsPatch;
    }
    
    @Override
    public Field patch(Field f, CPoolMap map){
        if(f == null)
            throw new NullPointerException("Cannot modify nonexistant field.");
        int flags = flagsPatch.patch(f.getAccessFlags());
        String signature = f.getSignature();
        if(sigPatch != null)
            signature = sigPatch;
        List<Attribute> attribs = attribsPatch.patch(
                new ArrayList<Attribute>(Arrays.asList(f.getAttributes())),
                map);
        Attribute[] attribArr = attribs.toArray(new Attribute[attribs.size()]);
        int nameIndex = Util.findConstantStringIn(map, name);
        int sigIndex = Util.findConstantStringIn(map, signature);
        Field newField = new Field(flags, nameIndex, sigIndex, attribArr,
                map.to);
        return newField;
    }
    
}
